<script setup lang="ts">
import type { Ref } from 'vue';
import dayjs from 'dayjs';
import { Search, Refresh } from '@element-plus/icons-vue';

import SubViewContainer from '@core/components/subLayout/SubViewContainer.vue';
import TableWithPagination from '@core/components/TableWithPagination.vue';
import * as Enums from '@firewall/engine/Enums';
import LogService, { FetchRawLogParams, RawLog, LogSetting } from '@firewall/engine/LogService';
import { FirewallSummary } from '@firewall/engine/Firewall';
import Application from '@firewall/engine/Application';
import Ips from '@firewall/engine/Ips';

const { t } = useI18n({ inheritLocale: true, useScope: 'local' });

const SearchInput = {
  SourceIp: 'sourceIp',
  DestinationIp: 'destinationIp',
  SourcePort: 'sourcePort',
  DestinationPort: 'destinationPort',
  RuleId: 'ruleId',
};
// 判断来源
const JudgeSource = {
  ALL: Enums.FILTER_ALL,
  AccessControl: 'accessControl',
  BlackList: 'blackList',
  WhiteList: 'whiteList',
};

const logs: Ref<Array<RawLog>> = ref([]);
const firewallInstance = reactive({} as Partial<FirewallSummary>);
const logSetting = reactive({} as Partial<LogSetting>);
const refreshLogSetting = async () => {
  if (!firewallInstance.firewallId) {
    return;
  }
  isTableLoading.value = true;
  try {
    const remoteSetting = await LogService.fetchSetting({ firewallId: firewallInstance.firewallId });
    Object.assign(logSetting, remoteSetting);
  } finally {
    isTableLoading.value = false;
  }
};

const filters = reactive({
  ipVersion: Enums.IpVersion.V4,
  timeRange: [
    dayjs().add(-1, 'days').format(Enums.REMOTE_DATE_FORMATTER),
    dayjs().format(Enums.REMOTE_DATE_FORMATTER),
  ],
  proto: Enums.AccessProtocol.All,
  searchRuleId: SearchInput.RuleId,
  app: Enums.FILTER_ALL,
  direction: Enums.NetworkDirection.All,
  action: Enums.AccessControlAction.All,
  sourceIp: '',
  destinationIp: '',
  sourcePort: '',
  destinationPort: '',
  ruleId: '',
  judgeSource: Enums.FILTER_ALL,
});
const pagination = reactive({
  pageSize: 10,
  currentPage: 1,
  total: 0,
});

const isTableLoading = ref(false);
const refreshTable = async () => {
  const fetchParams = getFetchParams();
  if (!fetchParams) {
    return;
  }
  isTableLoading.value = true;
  try {
    const response = await LogService.fetchRawLogs(fetchParams);
    logs.value = response.raw_logs || [];
    pagination.total = response.total || 0;
  } finally {
    isTableLoading.value = false;
  }
};
const getFetchParams = (): FetchRawLogParams | null => {
  if (!logSetting.logUserId || !logSetting.auditGroupId || !logSetting.auditEventId) {
    return null;
  }
  return {
    userid: logSetting.logUserId,
    loggroup: logSetting.auditGroupId,
    logunit: logSetting.auditEventId,
    page: pagination.currentPage,
    size: pagination.pageSize,
    start_time: dayjs(filters.timeRange[0]).format(Enums.REMOTE_DATE_FORMATTER),
    end_time: dayjs(filters.timeRange[1]).format(Enums.REMOTE_DATE_FORMATTER),
    is_full_index: Enums.RequestBoolean.True,
    judge_mode: Enums.LogJudgeMode.Equal,
    keys: [
      { key: 'ip_proto', value: filters.ipVersion, judge_mode: Enums.LogJudgeMode.Equal },
      { key: 'proto', value: filters.proto, judge_mode: Enums.LogJudgeMode.Equal },
      { key: 'app_id', value: filters.app, judge_mode: Enums.LogJudgeMode.Equal },
      { key: 'dir', value: filters.direction, judge_mode: Enums.LogJudgeMode.Equal },
      { key: 'action', value: filters.action, judge_mode: Enums.LogJudgeMode.Equal },
      { key: 'judgeSource', value: filters.judgeSource, judge_mode: Enums.LogJudgeMode.Equal },
      {
        key: 's_ip',
        value: filters.sourceIp,
        judge_mode: Enums.LogJudgeMode.FuzzyMatching,
      },
      {
        key: 'd_ip',
        value: filters.destinationPort,
        judge_mode: Enums.LogJudgeMode.FuzzyMatching,
      },
      {
        key: 's_port',
        value: filters.sourcePort,
        judge_mode: Enums.LogJudgeMode.FuzzyMatching,
      },
      {
        key: 'd_port',
        value: filters.destinationPort,
        judge_mode: Enums.LogJudgeMode.FuzzyMatching,
      },
      {
        key: 'rule_id',
        value: filters.ruleId,
        judge_mode: Enums.LogJudgeMode.FuzzyMatching,
      },
    ],
  };
};

const onFirewallChange = async (newInstance: FirewallSummary) => {
  Object.assign(firewallInstance, newInstance);
  await Application.getSummaryList();
  await Ips.getMethodSummaryList();
  await refreshLogSetting();
  await refreshTable();
};
const onFiltersChange = async () => {
  await refreshTable();
};

const router = useRouter();
const onClickDetail = (ruleId: string) => {
  router.push({
    name: 'logsAudit.accessRuleDetail',
    params: {
      firewallId: firewallInstance.firewallId,
      ruleId,
    },
  });
};

</script>

<template>
  <SubViewContainer>
    <ElCard v-loading="isTableLoading">
      <div class="table-controller">
        <FirewallInstanceSelector
          :firewall="firewallInstance"
          class="firewall-instance-selector"
          @update:firewall="onFirewallChange"
        />
        <div class="filters">
          <!-- TODO: 等防火墙支持 ipv6 后，重新开启 ip version 选择器 -->
          <!-- <IpVersionSelector v-model:ipVersion="filters.ipVersion" /> -->
          <ElDatePicker
            v-model="filters.timeRange"
            type="datetimerange"
            class="date-range-picker"
            :format="t('dateFormatter')"
            :value-format="Enums.REMOTE_DATE_FORMATTER"
          />
          <ProtoSelector v-model:proto="filters.proto" />
          <ApplicationSelector v-model:application="filters.app" />
          <NetworkDirectionSelector v-model:direction="filters.direction" />
          <ActionSelector v-model:action="filters.action" />
          <ElSelect
            v-model="filters.judgeSource"
            clearable
            :placeholder="t('judgeSourceSelect')"
            class="judge-source-selector">

            <ElOption
              v-for="item in JudgeSource"
              :key="item"
              :label="t(`judgeSource.${item}`)"
              :value="item"
            />
          </ElSelect>
          <ElInput
            v-model="filters.destinationPort"
            class="search-input"
            :placeholder="t('inputToSearchDestinationPort')"
            clearable
            @keyup.enter="onFiltersChange"
          />
          <ElInput
            v-model="filters.sourceIp"
            class="search-input"
            :placeholder="t('inputToSearchSourceIp')"
            clearable
            @keyup.enter="onFiltersChange"
          />
          <ElInput
            v-model="filters.destinationIp"
            class="search-input"
            :placeholder="t('inputToSearchDestinationIp')"
            clearable
            @keyup.enter="onFiltersChange"
          />
          <ElInput
            v-model="filters.sourcePort"
            class="search-input"
            :placeholder="t('inputToSearchSourcePort')"
            clearable
            @keyup.enter="onFiltersChange"
          />
          <ElInput
            v-model="filters.ruleId"
            class="search-input"
            :placeholder="t('inputToSearchRuleId')"
            clearable
            @keyup.enter="onFiltersChange"
          >
            <template #append>
              <ElButton :icon="Search" @click="onFiltersChange" />
            </template>
          </ElInput>
          <ElButton :icon="Refresh" @click="onFiltersChange" />
        </div>
      </div>
      <TableWithPagination
        v-model:paginationPageSize="pagination.pageSize"
        v-model:paginationCurrentPage="pagination.currentPage"
        size="small"
        :data="logs"
        :empty-text="t(firewallInstance.firewallId ? 'listIsEmpty' : 'noSelectFirewallYet')"
        :paginationTotal="pagination.total"
        :onUpdate:paginationPageSize="onFiltersChange"
        :onUpdate:paginationCurrentPage="onFiltersChange"
        paginationLayout="sizes, total, prev, pager, next"
      >
        <ElTableColumn prop="time" :label="t('hitTime')" min-width="120">
          <template #default="scope">
            {{ dayjs.unix(scope.row.time).format(t('dateFormatter'))}}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="s_ip" :label="t('sourceIp')" />
        <ElTableColumn prop="s_port" :label="t('sourcePort')" />
        <ElTableColumn prop="d_ip" :label="t('destinationIp')" />
        <ElTableColumn prop="d_port" :label="t('destinationPort')" />
        <ElTableColumn prop="proto" :label="t('proto')">
          <template #default="scope">
            {{ t(`protos.${scope.row.proto}`) }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="app_id" :label="t('application')">
          <template #default="scope">
            {{ Application.getOneById(scope.row.app_id)?.name || '----' }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="dir" :label="t('direction')">
          <template #default="scope">
            {{ t(`directions.${scope.row.dir}`) }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="action" :label="t('action')">
          <template #default="scope">
            {{ t(`actions.${scope.row.action}`) }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="rule_id" :label="t('hitRuleName')">
          <template #default="scope">
            {{ scope.row.rule_id }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="origin" :label="t('origin')">
          <template #default="scope">
            {{ t(`origins.${scope.row.origin}`) }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="operations" :label="t('operation')">
          <template v-slot="scope">
            <ElButton link
              type="primary"
              size="small"
              @click="onClickDetail(scope.row.rule_id)">{{t('detail')}}</ElButton>
          </template>
        </ElTableColumn>
      </TableWithPagination>
    </ElCard>
  </SubViewContainer>
</template>

<i18n lang="yaml" locale="zh-CN">
  dateFormatter: 'YYYY-MM-DD HH:mm'
  sourceIp: '源IP地址'
  destinationIp: '目的IP地址'
  sourcePort: '源端口'
  destinationPort: '目的端口'
  proto: '协议'
  protos:
    1: 'ICMP'
    6: 'TCP'
    17: 'UDP'
    58: 'ICMPv6'
  application: '应用'
  direction: '方向'
  directions:
    1: '内对外'
    2: '外对内'
  action: '动作'
  actions:
    1: '放行'
    2: '阻断'
  hitTime: '命中时间'
  hitRuleName: '命中规则名'
  origin: '判断来源'
  origins:
    3: '访问控制'
    4: '黑名单'
    5: '白名单'
  operation: '操作'
  detail: '详情'
  inputToSearchPlease: '输入搜索'
  listIsEmpty: '暂无数据'
  noSelectFirewallYet: '请先选择配置的防火墙实例'
  inputToSearchSourceIp: '输入源IP'
  inputToSearchDestinationIp: '输入目的IP'
  inputToSearchSourcePort: '输入源端口'
  inputToSearchDestinationPort: '输入目的端口'
  inputToSearchRuleId: '输入规则名称'
  judgeSourceSelect: '全部判断来源'
  judgeSource:
    all: '全部判断来源'
    accessControl: '访问控制'
    blackList: '黑名单'
    whiteList: '白名单'
</i18n>

<style lang="scss" scoped>
.table-controller {
  display: flex;
  flex-direction: column;

  .firewall-instance-selector {
    margin-bottom: 10px;
  }

  .filters {
    display: flex;
    flex-flow: wrap;
    align-self: flex-end;
    justify-content: flex-end;
    max-width: 1200px;

    & > :deep(*) {
      flex-grow: 0;
      margin: 0 0 10px 10px;
    }

    .filter-placeholder {
      width: 138px;
      height: 32px;
      border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
      box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
    }
  }

  .date-range-picker {
    width: 140px;
  }

  .judge-source-selector {
    width: 160px;
  }

  .search-input {
    width: 160px;

    :deep(.el-input__inner) {
      line-height: var(--el-input-height);
    }
  }
}
</style>
